(function(){
/**
 * jProton JavaScript Framework, version 0.3.0a
 * Copyright (c) 2008 Pedro Simonetti Garcia
 * 
 * Version - 0.3.0a
 * $Revision: 246 $
 * $Date: 2008-03-26 05:20:18 +0000 (Wed, 26 Mar 2008) $
 * 
 * Project site: http://code.google.com/p/jproton/
 * Licensed under MIT: http://www.opensource.org/licenses/mit-license.php
 */

/**
 * Namespace central do framework. "jProton" é um namespace executável, de modo
 * que ele é tanto um agregador de métodos/atributos quanto uma função. Como
 * namespace, ele é o agregador de todas funcionalidades do framework. Como
 * função, ele é um atalho para o principal recurso do framework: a seleção de
 * elementos do "documento". Além disso, o namespace "$" é um atalho para 
 * "jProton", de modo que os dois códigos abaixo:
 * 
 * ~~
 * // Usa o namespace jProton para instanciar a classe jProton.Set
 * jProton(selector, context)
 * // Usa o atalho $ para instanciar a classe jProton.Set
 * $(selector, context)
 * ~~
 * 
 * equivalem a:
 * 
 * ~~
 * // Instancia uma nova classe jProton.Set explicitamente 
 * new jProton.Set().select(selector, context)
 * ~~
 * 
 * A maioria dos recursos do framework encontram-se dentro de outros namespaces
 * dentro de "jProton", como "jProton.Set", ou "jProton.ajax". Mas, alguns 
 * recursos especiais encontram-se no próprio namespace "jProton", como os 
 * métodos "jProton.trim"() e "jProton.each"(), acessíveis também como 
 * "$.trim()" e "$.each()".
 *  
 * @name jProton
 * @namespace
 * @extends core
 *//** @ignore */
window.jProton = function(selector, context) {
  return new jProton.Set().select(selector, context);
};

/** 
 * Atalho para o namespace "jProton". Considerando que "jProton" é um 
 * namespace executável, ou seja, ele é um namespace e função ao mesmo tempo,
 * o atalho "$" pode ser usado para ambos os casos. Ou seja, é possível usá-lo 
 * como namespace para acessar recursos do framework, como a método "$.trim()", 
 * o namespace "$.ajax", ou a classe "$.Set". É possível também usá-lo como 
 * função, no formato "$()", o que consiste no atalho para instanciar uma 
 * classe "jProton.Set".
 * 
 * @function
 * @name $
 * @param {String}  selector  Seletor usado para selecionar os elementos.
 * @param {Element} [context] Contexto usado na busca. O padrão é "document".
 * @return {jProton.Set}
 * @see jProton
 * @extends core 
 */
window.$ = jProton;


/**
 * Cria um namespace, e retorna o último objeto desse namespace.
 * 
 * *Exemplo:*
 * ~~
 * // Cria o namespace 'my.name.space' caso não exista, e retorna o último 
 * // objeto do namespace, no caso o objeto 'space'.
 * $.namespace('my.name.space');
 * ~~
 * 
 * @function
 * @param {Object}  ns   Namespace a ser criado.
 */
jProton.namespace = function(ns) {
  if (!ns)
    return;

  var n = ns.split('.');
  var target = window, o;
  
  for (var i = 0, ni; ni = n[i]; i++) {
    target = (o = target[ni]) ? o : target[ni] = {};
  }

  return target;
};


/**
 * Extende as propriedades de um ou mais objetos-fonte para outro objeto-alvo. 
 * Se apenas um objeto for passado como parâmetro, ele será considerado o
 * objeto-fonte, e o objeto referenciado por "this" será considerado o alvo,
 * que nesse caso será o namespace "jProton". Se mais de um objeto for
 * passado como parâmetro, o primeiro objeto será considerado o alvo, e os 
 * objetos restantes serão considerados as fontes.
 * 
 * Ao final, será retornado o objeto-alvo extendido.
 * 
 * ===Sintaxe:===
 * ~~
 * jProton.extend( source || ( target, source1 [ , source2, ... ] ) );
 * ~~
 * 
 * Existem três propriedades que possuem significado especial quando 
 * definidas no objeto fonte. Elas são: "@init", "@extension" e "@require".
 * Essas propriedades não serão extendidas para o objeto alvo, servindo apenas
 * para definir respectivamente a inicialização, registro e dependência da 
 * extensão.
 * 
 * *NOTA: A maneira de declarar as extensões e suas dependências será 
 * diferente na versão 0.4.x, e por consequência, as propriedades
 * "@extension" e "@require" entrarão em desuso.*
 * 
 * ==Inicialização de Extensões==
 * 
 * Algumas extensões necessitam que um determinado código seja executado 
 * assim que a extensão for criada. Para isso, deve-se informar no objeto
 * fonte a ser inicializado a propriedade especial "@init", com a devida
 * função de inicialização como seu valor.
 * 
 * ==Registro de Extensões==
 * 
 * Além disso, o método "jProton.extend()" pode ser usado também para registrar 
 * extensões automaticamente. Para isso, deve-se informar uma propriedade 
 * especial no objeto fonte com nome "@extension". O valor dessa propriedade é 
 * um objeto que especifica o nome e versão da extensão, no seguinte formato:
 * 
 * ~~
 *   '@extension': {'name': 'version'}
 * ~~
 * 
 * O registro é feito definindo uma propriedade com nome "name" e valor 
 * "version" no namespace "extensions" do objeto alvo. Repare que se um
 * namespace for informado como alvo, a extensão será registrada no último
 * objeto do namespace, como exemplificado abaixo:
 * 
 * ~~
 *   target.namespace.extensions.name = 'version';
 * ~~
 * 
 * 
 * ==Dependência de Extensões==
 * 
 * Se a extensão que está sendo criada usa recursos de uma outra extensão,
 * deve-se usar a propriedade especial "@require" para a dependência com
 * outra extensão, evitando assim que a extensão seja carregada caso a
 * extensão requirida não esteja instalada.
 * 
 * Se a extensão requerida não estiver instalada, será gerado um erro 
 * indicando que a extensão deve ser instalada. Se a extensão requerida 
 * estiver instalada, mas for de uma versão inferior a versão requerida, 
 * será gerado um erro indicando a versão instalada é inferior a versão
 * requerida.
 * 
 * O exemplo a seguir ilustra o uso das três propriedades especiais para
 * se criar uma extensão de "jProton". Será criada a extensão "myExt" versão
 * "0.1" no namespace "jProton.myPlugin" apenas se a extensão "myOtherExt" 
 * versão "0.2" já estiver instalada em "jProton.myPlugin". Se a extensão
 * requerida estiver instalada, será criado o namespace "jProton.myPlugin", 
 * e adicionado a ele a propriedade "newProperty" e o método "newMethod". Além 
 * disso, como esta sendo informado a propriedade "@extension", será criada 
 * uma nova propriedade "jProton.myPlugin.extensions.myExt" com o valor "0.1". 
 * Ao final, será retornado o objeto alvo raiz, no caso, o objeto "jProton".
 * 
 * *Exemplo 2:*
 * ~~
 * // Cria o namespace onde do plugin
 * $.namespace('jProton.myPlugin');
 * 
 * // Cria uma nova extensão no namespace 'jProton.myPlugin'. Será criada
 * // uma nova propriedade 'jProton.myPlugin.extensions.myExt' com o valor
 * // '0.1' indicando o registro e versão da extensão.
 * $.extend(jProton.myPlugin, {
 * 
 *   // declara a dependência com outra extensão
 *   '@require': {'myOtherExt': '0.2'},
 *   
 *   // registra a extensão  
 *   '@extension': {'myExt': '0.1'},
 *   
 *   // Função de inicialização
 *   '@init': function() {
 *     // O código de inicialização segue aqui
 *     ...
 *   },
 *    
 *   // nova propriedade a ser extendida
 *   newProperty: 'value',
 *    
 *   // novo método a ser extendido
 *   newMethod: function() {
 *     return this.newProperty;
 *   }
 *   
 * });
 * ~~
 * 
 * @function
 * @param {Object} [target]  Objeto alvo para onde as propriedades serão
 *                           copiadas.
 * @param {Object}  source   Objeto fonte de onde as propriedades serão
 *                           copiadas.
 * @return {Object} Retorna o objeto "target" extendido.
 * @extends core
 * @see jProton.namespace
 */
jProton.extend = function() {
  var target = this, t = target, source, p, o,
      a0 = arguments[0], ai = 0, al = arguments.length;

  // processa o target
  if (al > 1) {
    ai++;
    target = t = a0;
  }

  // processa a extensão para cada objeto fonte
  for(; source = arguments[ai]; ai++)

    // para cada propriedade do objeto fonte
    for(var prop in source) {
      p = source[prop];
      
      // herda as propriedades do objeto fonte
      if (prop.charAt(0) != '@') {
        
        // se a propriedade for um array, extende esse array. Ou seja, se
        // já existir algum array na propriedade do objeto destino, concatena 
        // com o outro array. Se não existir, copia o array.
        if (p && p.splice) {
          var tp = t[prop];
          t[prop] = tp && tp.splice ? tp.concat(p) : new Array().concat(p);

        // se a propriedade for um objeto, extende esse objeto. Ou seja, se
        // já existir algum objeto na propriedade do objeto destino, extende o
        // objeto. Se não existir, cria uma cópia do objeto.
        } else if (p && typeof p == 'object') {
          t[prop] = jProton.extend(t[prop] || {}, p);

        // se for uma propriedade comum, copia seu valor
        } else {
          t[prop] = p;
        }
        
      } else {
        // processa a dependência de módulos
        if (prop == '@init') {
          p.call(t);
          
        // processa a dependência de módulos
        } else if (prop == '@require') {
          for (var ext in p) {

            // versão requerida
            var v = p[ext];
            
            // verifica se extensão requerida está instalada
            o = t.extensions[ext] || null;

            // confirma a existência e versão da extensão
            if (!o)
              throw 'A extensão "'+ext+'" deve estar instalada.';

            else if (o < v && v != 'all')
              throw 'A extensão "'+ext+'" deve ter a versão '+v+' ou superior.';
          }

        // processa o registro da extensão
        } else if (prop == '@extension') {

          // registra a extensão no namespace "extensions" do objeto
          o = t.extensions || (t.extensions = {});
          for (var name in p) {
            o[name] = p[name];
          }
        }
      }
    }
  // retorna o objeto alvo extendido
  return target;
};


// Extensão central de jProton.
/**#@+ @extends core */
jProton.extend(/** @lends jProton */ {

  '@extension': {'core': '0.3.0a'},

  /**
   * Atalho para a função "document.getElementById()".
   *  
   * @param {String} id
   * @return {Element}
   */
  id: function(id) {
    return document.getElementById(id);
  },

  /**
   * Executa uma função para cada elemento de um array.
   * 
   * ~~
   * // Exibe os elementos de um array
   * $.each(array, function(i,e) {
   *   alert('O elemento de número ' + i + ' é igual a ' + e);
   * });
   * ~~
   * 
   * @param {Array}    a   Array a ser percorrido.
   * @param {Function} fn  Função a ser chamada para cada elemento.
   */
  each: function(a, fn) {
    for(var i=0, ai; ai=a[i]; i++)
      fn.call(ai, i, ai);
  },

  /**
   * Remove os espaços laterais de uma string.
   * 
   * @param {String}  s  String a ser alterada.
   * @return {String}
   */
  trim: function(s) {
    return s.replace(/^\s*|\s*$/g, '');
  },

  /** 
   * Função vazia usada para remover funções adicionadas a elementos.
   */
  emptyFn: function() {}

  /**#@-*/
});


/**
 * Principal classe do framework, que permite a seleção e manipulação 
 * simultânea de conjuntos de elementos. Na prática, você raramente irá 
 * instanciar manualmente uma classe "jProton.Set". Ao invés disso, é 
 * mais conveniente usar a função "jProton()", ou o atalho "$()", pois os dois 
 * códigos abaixo:
 * 
 * ~~
 * // Usa o namespace jProton para instanciar a classe jProton.Set
 * jProton(selector, context)
 * // Usa o namespace alternativo $ para instanciar a classe jProton.Set
 * $(selector, context)
 * ~~
 * 
 * equivalem a:
 *    
 * ~~
 * // Instancia explicitamente um novo objeto da classe jProton.Set 
 * new jProton.Set().select(selector, context)
 * ~~
 * @extends core
 * @class Principal classe do framework, que permite a seleção e manipulação 
 * simultânea de conjuntos de elementos.
 */
jProton.Set = function() {};

// A classe jProton.Set é uma sublcasse de Array
jProton.Set.prototype = new Array();

/**
 * Atalho para incluir funcionalidades na classe jProton.Set.
 * 
 * @type {jProton.Set}
 * @extends core
 */
jProton.set = jProton.Set.prototype;

// Atalho para jProton.set
var set = jProton.set;

// Adiciona a função extend para a classe jProton.Set
set.extend = jProton.extend;


// Extensão central da classe jProton.Set.
/**#@+ @extends core */
set.extend(/** @lends jProton.Set.prototype */ {

  '@extension': {'core': '0.3.0a'},

  /** 
   * Referência para o último conjunto da cadeia de operações.
   * 
   * @type jProton.Set
   */
  _lastSet: null,

  /** 
   * Versão do framework.
   * 
   * @type String
   */
  jproton: 'jProton.0.3.9a',

  /**
   * Método privado usado para ligar diferentes operações em cadeia. Será 
   * instanciado um novo objeto da classe "jProton.Set", vazio, ligado ao 
   * objeto atual pela referência do parâmetro "jProton.Set._lastSet". Caso 
   * seja informado algum array de elementos, o novo objeto será preenchido 
   * com esse conjunto de elementos.
   * 
   * @param {Array} [e] Array de elementos
   * @return {jProton.Set}
   */
  _chain: function(e) {
    var r = new jProton().copy(e);

    r._lastSet = this;

    return r;
  },

  /**
   * Adiciona os elementos que correspondem ao seletor informado. O parâmetro
   * seletor indica quais elementos serão selecionados, enquanto que o 
   * parâmetro contexto indica nos descendentes de qual elemento será será
   * feita a busca. Se nenhum contexto for informado, ele será considerado o
   * "document", ou seja, a busca ocorrerá em todo o documento. Opcionalmente,
   * pode-se passar um seletor como contexto, e nesse caso, o primeiro elemento
   * selecionado será usado como contexto.
   * 
   * @param {String}  selector  Seletor que descreve os elementos a serem 
   *                            escolhidos.
   * @param {Element} [context] Contexto usado na busca. O padrão é "document".
   * @return {jProton.Set}
   */
  select: function(selector, context) {
    var type = typeof selector;

    // processa $(expr)
    if (type == 'string') {
      return this.copy(query.select(selector, context));

    // processa $(obj)
    } else if (type == 'object') {

      // processa $(element)
      if (selector.nodeName) {
        this.push(selector);

      // processa $(array-like)
      } else if (selector.length) {
        return this.copy(selector);
      }
    }

    return this; 
  },
  
  /**
   * Adiciona novos elementos ao conjunto.
   * 
   * @param {String}  selector   Seletor que descreve os elementos a serem 
   *                             adicionados.
   * @param {Element} [context]  Contexto usado na busca. O padrão é "document".
   * @return {jProton.Set}
   */
  add: function(selector, context) {
    return this._chain(this).select(selector, context);
  },

  /**
   * Procura por elementos dentro dos elementos selecionados. O conjunto atual
   * de elementos selecionados será usado como contexto de uma nova busca.
   * 
   * @param {String}  selector   Seletor que descreve os elementos a serem 
   *                             pesquisados.
   * @return {jProton.Set}
   */
  find: function(selector) {
    return this._chain().select(selector, this);
  },

  /**
   * Filtra os elementos selecionados deixando apenas os que satisfaçam ao 
   * seletor.
   * 
   * @param {String}  selector   Seletor que descreve os elementos a serem 
   *                             filtrados.
   * @return {jProton.Set}
   */
  filter: function(selector) {
    return this._chain().copy(query.filter(this, selector));
  },
  
  /**
   * Filtra os elementos selecionados removendo aqueles que satisfaçam ao 
   * seletor.
   * 
   * @param {String}  selector   Seletor que descreve os elementos a serem 
   *                             filtrados.
   * @return {jProton.Set}
   */
  not: function(selector) {
    return this._chain().copy(query.filter(this, selector, true));
  },
  
  /**
   * Procura pelo próximo irmão de cada elemento selecionado que satisfaça ao 
   * seletor.
   * 
   * @param {String}  selector   Seletor que descreve os elementos a serem 
   *                             encontrados.
   */
  next: function(selector){
    for(var i=0, l=this.length; i<l; i++)
      while((this[i] = this[i].nextSibling) && this[i].nodeType != 1);

    return selector ? this.filter(selector) : this;
  },

  /**
   * Procura pelo irmão anterior de cada elemento selecionado que satisfaça ao 
   * seletor.
   * 
   * @param {String}  selector   Seletor que descreve os elementos a serem 
   *                             encontrados.
   */
  prev: function(selector){
    for(var i=0, l=this.length; i<l; i++)
      while((this[i] = this[i].previousSibling) && this[i].nodeType != 1);

    return selector ? this.filter(selector) : this;
  },

  /**
   * Encerra a operação atual e retorna o último objeto "jProton.Set" utilizado
   * na cadeia.
   * 
   * @return {jProton.Set}
   */
  end: function() {
    return this._lastSet || new jProton.Set();
  },

  /**
   * Executa uma função para cada elemento selecionado.
   * 
   * ~~
   * // Exibe o conteúdo de cada elemento H1 existente no documento.
   * $('h1').each(function(i){
   *   alert(this.innerHTML);
   * });
   * ~~
   *    
   * @param {Function} fn  Função a ser executada para cada elemento 
   *                       selecionado. Essa função é chamada passando como
   *                       parâmetro o índice do elemento. O elemento por sua
   *                       vez é acessado pela palavra-chave "this".
   * @return {jProton.Set}
   */
  each: function(fn) {
    for(var i = 0, ei; ei = this[i]; i++)
      fn.call(ei, i);

    return this;
  },

  /**
   * Copia todos elementos de um array.
   * 
   * @param {Array}  a  Array a ser copiado.
   * @return {jProton.Set}
   */
  copy: function(a) {
    if (a) {
      if (a.splice) {
        this.push.apply(this, a);

      } else {
        for(var i = 0, ai; ai = a[i]; i++) {
          this.push(ai);
        }
      }
    }
    return this;
  }

  /**#@-*/
});


/**
 * Namespace responsável pela manipulação de elementos DOM.
 * 
 * @name jProton.element
 * @namespace
 */
jProton.namespace('jProton.element');

// Atalho para jProton.element
var element = jProton.element;


// Extensão dom para jProton.element.
/**#@+ @extends dom */
jProton.extend(element, /** @lends jProton.element */ {
  '@extension': {'dom': '0.3.0a'},
  
  /**
   * Retorna o valor da propriedade do elemento.
   *      
   * @name attr
   * @param  {Element}  e     Elemento alvo.         
   * @param  {String}   key   Nome da propiedade.
   * @return {Value}
   */
     
  /**
   * Altera o valor da proriedade do elemento.
   * 
   * @param  {Element}  e     Elemento alvo.         
   * @param  {String}   key   Nome da propiedade.
   * @param  {Value}    val   Valor a ser definido.
   */
  attr: function(e, key, val) {
    return e ? val ? e[key] = val : e[key] : undefined;
  },
  
  /**
   * Retorna o valor da propriedade 'value' do elemento.
   *    
   * @name val
   * @memberOf jProton.element;
   * @param  {Element}  e     Elemento alvo.         
   * @return {Value}
   */
   
  /**
   * Altera valor da propriedade "value" do elemento. Se o elemento em questão 
   * for do tipo "select options", a opção com o mesmo valor será selecionada.  
   *    
   * @name val
   * @memberOf jProton.element;
   * @param {Element} e    elemento alvo.         
   * @param {String}  val  valor a ser atrubuído a propriedade "value".
   */
           
  /**
   * Marca ou seleciona elementos do tipo "select options", "checkbox" ou 
   * "radio button".
   * 
   * ~~
   * // Seleciona as opções 'Multiple2' e 'Multiple3' do select de id 'multiple'   
   * $('#multiple').val(['Multiple2', 'Multiple3']);
   *    
   * // Marca o checkbox de valor 'check2' e o radio button de valor 'radio1'
   * $('input').val(['check2', 'radio1']);
   * ~~
   * 
   * @param {Element} e        Elemento alvo.         
   * @param {Array}   values   Array de Strings com os valores dos elementos
   *                           que serão marcados ou selecionados.
   *                        
   * @return {jProton.query}
   */
  val: function(e, val) {
    if (e) {
      // processa $().val(val)
      if (val) {
        var valType = typeof val;
  
        // processa $().val(String)
        if (valType == 'string')
          e.value = val;
  
        // processa $().val(Array)
        else if (valType == 'object') {
          var vl = val.length, tag, type;
  
          // seleciona um ou mais elementos 'checkbox' ou 'radio'
          if ((tag = e.nodeName.toLowerCase()) == 'input' && 
              ((type = e.type) == 'checkbox' || type == 'radio')) {
            e.checked = false;
            for (var j=0; j < vl; j++)
              if (e.value == val[j])
                e.checked = true;
  
          // seleciona uma ou mais opções do elemento 'select'
          } else if (tag == 'select') {
            for (var o=0, op, op=e.options, ol=o.length; o<ol; o++) {
              if (e.multiple) 
                op[o].selected = false;
  
              for (var j=0; j < vl; j++)
                if (op[o].value == val[j])
                  op[o].selected = true;
            }
          }
        }
      // processa $().val()
      } else {
        // se for um elemento comum, retorna seu valor
        if (e.nodeName.toLowerCase() != 'select')
          return e.value;
  
        // se for um elemento 'select' retorna as opções selecionadas
        else {
          var r = new Array();
          for (var i=0, o=e.options, ol=o.length; i<ol; i++)
            if (o[i].selected)
              r.push(o[i].value);
  
          return e.multiple ? r : r[0];
        }
      }
    } else
      return undefined;
  },
  
  /**
   * Retorna o conteúdo de texto do elemento.
   *    
   * @name text
   * @param  {Element} e   Elemento alvo.         
   * @return {String}
   */

  /**
   * Aletar o conteúdo de texto do elemento.
   * 
   * @name text
   * @param {Element} e    Elemento alvo.         
   * @param {String}  val  Conteúdo de texto a ser atribuído.
   * @return {jProton.query}
   */
  text: (function() {
    var txt = typeof document.textContent != 'undefined' ?
        'textContent' : 'innerText';

    var text = function(e, val) {
      return e ? val ? e[txt] = val : e[txt] : undefined;
    };

    return text;
  })()

  /**#@-*/
});


// Extensão dom para jProton.Set.
/**#@+ @extends dom */
set.extend(/** @lends jProton.Set.prototype */ {

  '@extension': {'dom': '0.3.0a'},

  /**
   * Retorna o valor da propriedade do primeiro elemento selecionado. Se a 
   * propriedade não existir, retorna undefined.   
   * 
   * @param  {String}   key   Nome da propiedade.
   * @return {Value}
   */

  /**
   * Altera o valor da proriedade de todos os elementos selecionados.
   * 
   * @param  {String}   key   Nome da propiedade.
   * @param  {Value}    val   Valor a ser definido.
   * @return {jProton.Set}
   */
  attr: function(key, val) {
    if (val) {
      // Processa $().attr(key, val)
      for(var i = 0, ei; ei = this[i]; i++)
        element.attr(ei, key, val);

      return this;
    } else
      // Processa $().attr(key)
      return element.attr(this[0], key);
  },

  /**
   * Retorna o valor da propriedade 'value' do primeiro elemento selecionado.
   * 
   * @return {Value}
   */

  /**
   * Altera valor à propriedade "value" de todos elementos selecionados. Se
   * o elemento em questão for do tipo "select options", a opção com o mesmo
   * valor será selecionada.  
   * 
   * ~~
   * // Seleciona a opção que possui valor 'op1' de todos elementos 'select'   
   * $('select').val('op1');
   * ~~
   * 
   * @param {String}  val  Valor a ser atrubuído a propriedade "value".
   * @return {jProton.Set}
   */

  /**
   * Marca ou seleciona elementos do tipo "select options", "checkbox" ou 
   * "radio button".
   * 
   * ~~
   * // Seleciona as opções 'Multiple2' e 'Multiple3' do select de id 'multiple'   
   * $('#multiple').val(['Multiple2', 'Multiple3']);
   *    
   * // Marca o checkbox de valor 'check2' e o radio button de valor 'radio1'
   * $('input').val(['check2', 'radio1']);
   * ~~
   *
   * @param {Array}   values   Array de Strings com os valores dos elementos
   *                           que serão marcados ou selecionados.
   *                        
   * @return {jProton.Set}
   */
  val: function(val) {
    if (val) {
      // Processa $().val(val)
      for(var i = 0, ei; ei = this[i]; i++)
        element.val(ei, val);

      return this;
    } else
      // Processa $().val()
      return element.val(this[0]);
  },

  /**
   * Retorna o conteúdo HTML do primeiro elemento selecionado.
   * 
   * @return {String}
   */

  /**
   * Aletar o conteúdo HTML de todos elementos selecionados.
   * 
   * @param {String}  val   Conteúdo HTML a ser atribuído.
   * @return {jProton.Set}
   */
  html: function(val) {
    if (val) {
      // Processa $().html(val)
      for(var i = 0, ei; ei = this[i]; i++)
        ei.innerHTML = val;

      return this;
    } else
      // Processa $().html()
      return this[0] ? this[0].innerHTML : undefined;
  },

  /**
   * Adiciona o conteúdo HTML no final do interior de todos elementos 
   * selecionados.
   * 
   * @param {String}  val   Conteúdo HTML a ser adicionado.
   * @return {jProton.Set}
   */
  append: function(val) {
    for(var i = 0, ei; ei = this[i]; i++)
      ei.innerHTML += val;

    return this;
  },

  /**
   * Adiciona o conteúdo HTML no início do interior de todos elementos 
   * selecionados.
   * 
   * @param {String}  val   Conteúdo HTML a ser adicionado.
   * @return {jProton.Set}
   */
  prepend: function(val) {
    for(var i = 0, ei; ei = this[i]; i++)
      ei.innerHTML = val + ei.innerHTML;

    return this;
  },

  /**
   * Retorna o conteúdo de texto do primeiro elemento selecionado.
   * 
   * @return {String}
   */

  /**
   * Altera o conteúdo de texto de todos os elementos selecionados.
   *
   * @param {String}  val  Conteúdo de texto a ser atribuído.
   * @return {jProton.Set}
   */
  text: function(val) {
    if (val) {
      // Processa $().text(val)
      for(var i = 0, ei; ei = this[i]; i++)
        element.text(ei, val);

      return this;
    } else
      // Processa $().text()
      return element.text(this[0]);
  }

  /**#@-*/
});


// Extensão css para jProton.
/**#@+ @extends css */
jProton.extend(element, /** @lends jProton.element */ {

  '@extension': {'css': '0.3.0a'},

  /**
   * Verifica se o elemento possui uma determinada classe.
   * 
   * @param {Element}  e      Elemento alvo.         
   * @param {String}   name   Nome da classe.
   */
  hasClass: function(e, name) {
    return (' '+e.className+' ').indexOf(' '+name+' ') != -1;
  },

  /**
   * Processa a lógica de addClass() para um único elemento.
   *
   * @param {Element}  e      Elemento alvo.         
   * @param {String}   name   Nome da classe.
   */
  addClass: function(e, name) {
    // cópia de performance de jProton.hasClass(e, name)
    if ((' '+e.className+' ').indexOf(' '+name+' ') == -1)
      e.className = e.className ? e.className + ' ' + name : name; 
  },

  /**
   * Processa a lógica de removeClass() para um único elemento.
   * 
   * @param {Element}  e      Elemento alvo.         
   * @param {String}   name   Nome da classe.
   */
  removeClass: function(e, name) {
    e.className = (' ' + e.className + ' ').
        replace(new RegExp('(\\S*)\\s+'+name+'\\s+(\\S*)', 'g'), '$1 $2').
        replace(/^\s*|\s*$/g, '');
  },

  /**
   * Alterna o uso de uma classe em todos os elementos selecionados.
   *
   * @param {Element} e      Elemento alvo.         
   * @param {String}  name   Nome da classe.
   */
  toggleClass: function(e, name) {
    // cópia de performance de jProton.hasClass(e, name)
    if ((' '+e.className+' ').indexOf(' '+name+' ') >= 0)
      this.removeClass(e, name)
    else
      this.addClass(e, name);
  },

  /**
   * Retorna o valor da propriedade de estilo do elemento.
   * Baseado em: [[http://www.quirksmode.org/dom/getstyles.html|quirksmode]].
   * 
   * @param {Element} e      Elemento alvo.         
   * @param {String}  name   Nome da propriedade de estilo.
   * @param {String}  val    Valor a ser atribuído.
   */
  css: function(e, name, val) {
    if (e) {
      if (val) 
        // processa css(name, val)
        e.style[name] = val;

      else {
        var type = typeof name;

        if (type == 'object')
          // processa css(e, {name1: val1, name2: val2})
          for (p in name)
            e.style[p] = name[p]

        else
          // processa css(e, name)
          return window.getComputedStyle ? // Tenta o modo Mozilla
              document.defaultView.getComputedStyle(e,null)[name] || 
              e.style[name] :          // Ou então, tenta a propriedade style
              e.currentStyle ?         // Tenta o modo IE
              e.currentStyle[name] ||  // Se não der certo,
              e.style[name] :          // tenta a propriedade style. Senão,
              undefined;               // Não foi possível recuperar o estilo
      }
    } else
      return undefined;
  }

  /**#@-*/
});


// Extensão css para jProton.Set.
/**#@+ @extends css */
set.extend(/** @lends jProton.Set.prototype */ {

  '@extension': {'css': '0.3.0a'},

  /**
   * Verifica se o primeiro elemento selecionado possui uma determinada classe.
   *
   * @param {String}  name   Nome da classe.
   * @return {Boolean}
   */
  hasClass: function(name) {
    return this[0] ? 
      // cópia de performance de jProton.hasClass(e, name)
      (' '+this[0].className+' ').indexOf(' '+name+' ') != -1 : 
      undefined;
  },

  /**
   * Adiciona a classe a todos elementos selecionados.
   * 
   * @param {String}  name   Nome da classe.
   * @return {jProton.Set}
   */
  addClass: function(name) {
    for(var i = 0, ei; ei = this[i]; i++)
      element.addClass(ei, name);

    return this;
  },

  /**
   * Remove a classe de todos os elementos selecionados.
   * 
   * @param {String}  name   Nome da classe.
   * @return {jProton.Set}
   */
  removeClass: function(name) {
    for(var i = 0, ei; ei = this[i]; i++)
      element.removeClass(ei, name);

    return this;
  },

  /**
   * Alterna o uso de uma classe em todos os elementos selecionados.
   * 
   * @param {String}  name   Nome da classe.
   * @return {jProton.Set}
   */
  toggleClass: function(name) {
    for(var i = 0, ei; ei = this[i]; i++)
      element.toggleClass(ei, name);

    return this;
  },

  /**
   * Retorna o valor da propriedade de estilo do primeiro elemento selecionado.
   * 
   * @param {String}  name   Nome da propriedade de estilo.
   * @return {Value}   
   */

  /**
   * Altera os valores de um conjunto de propriedades de todos os elementos 
   * selecionados.
   * 
   * @param {Object}  properties   Objeto contendo pares de propriedade/valor.
   * @return {jProton.Set}
   */

  /**
   * Altera o valor da propriedade de estilo de todos os elementos selecionados.
   * Baseado em: [[http://www.quirksmode.org/dom/getstyles.html|Quirksmode]].
   * 
   * @param {String}  name   Nome da propriedade de estilo.
   * @param {String}  val    Valor da propriedade.
   * @return {jProton.Set}
   */
  css: function(name, val) {
    if (val) {
      // Processa $().css(name, val)
      for(var i = 0, ei; ei = this[i]; i++)
        element.css(ei, name, val);

      return this;
    } else
      // Processa $().css(name)
      return element.css(this[0], name);
  }

  /**#@-*/
});


/**
 * Namespace responsável pela manipulação de eventos. Adaptado a partir de: 
 * [[http://www.quirksmode.org/blog/archives/2005/10/_and_the_winner_1.html|Quirksmode]].
 *  
 * @name jProton.event
 * @namespace
 */
/**#@+ @extends event */
jProton.event = function(){
  
  /**
   * Lista de eventos registrados
   * @private
   * @memberOf jProton.event
   * @type Object[]
   */
  var _events = new Array();
  var addEventListener = typeof document.addEventListener != 'undefined';
  var attachEvent = typeof document.attachEvent != 'undefined';
  
  return /** @lends jProton.event */ {
    version: 'jProton.event.0.3.9a',
  
    /**
     * Adiciona o tratamento de um evento a um elemento.
     * 
     * @param {Element}  e     Elemento alvo.
     * @param {String}   type  Tipo de evento a ser adicionado.
     * @param {Function} fn    Função de tratamento do evento.   
     */
    add: function(e, type, fn){
      _events.push({e: e, type: type, fn: fn});
  
      if (addEventListener)
        e.addEventListener(type, fn, false)

      else if (attachEvent) {
        e['e'+type+fn] = fn;
        e[type+fn] = function() { e['e'+type+fn](window.event); }
        e.attachEvent('on'+type, e[type+fn]);
      }
    },
  
    /**
     * Remove o tratamento de um evento de um elemento.
     * 
     * @param {Element}  e     Elemento alvo.
     * @param {String}   type  Tipo de evento a ser removido.
     * @param {Function} fn    Função de tratamento do evento.   
     */
    remove: function(e, type, fn){
      var l, fs = fn.toString();

      // Remove o evento da lista de eventos
      for (var i=0,li; li=_events[i]; i++)
        if (li.e==e && li.type==type && (li.fn==fn || li.fn.toString()==fs) ) {
          l = _events.splice(i,1)[0];
          break;
        }

      if (l) {
        // tenha certeza que esta apontado para a função original
        fn = l.fn;
        if (addEventListener)
          e.removeEventListener(type, fn, false)

        else if (attachEvent) {
          e.detachEvent('on'+type, e[type+fn]);
          e[type+fn] = null;
          e['e'+type+fn] = null;
        }
      }
    },
  
    /**
     * Remove todos os eventos adicionados.
     */     
    removeAll: function() {
      for (var i=0; li=_events[i]; i++)
        this.remove(li.e, li.type, li.fn);
    }
  }

  /**#@-*/
}();


// Extensão event para jProton.Set.
/**#@+ @extends event */
set.extend(/** @lends jProton.Set.prototype */ {

  '@extension': {'event': '0.3.0a'},

  /**
   * Trata um determinado evento em todos os elementos selecionados.
   * 
   * @param {String}   type  Tipo de evento a ser tratado.
   * @param {Function} fn    Função de tratamento do evento.
   * @return {jProton.Set}
   */
  on: function(type, fn){
    return this.each(function(){
      jProton.event.add(this, type, fn);
    });
  },

  /**
   * Cancela o tratamento um determinado evento em todos os elementos 
   * selecionados.
   *
   * @param {String}   type  Tipo de evento a ser tratado.
   * @param {Function} fn    Função de tratamento do evento.
   * @return {jProton.Set}
   */
  un: function(type, fn){
    return this.each(function(){
      jProton.event.remove(this, type, fn);
    });
  }

  /**#@-*/
});


/**
 * Namespace responsável pelo controle de recursos AJAX.
 * 
 * @namespace
 */
jProton.ajax = function(options) {
  return this.ajax.request(options);
};

// Atalho para jProton.ajax
var ajax = jProton.ajax;

jProton.extend(ajax, function() {

  /**#@+
   * @memberOf jProton.ajax  
   * @extends ajax   
   */

  /** 
   * Lista de requisições pendentes
   * @name _requests
   * @type request[]
   */
  var _requests = new Array();

  /** 
   * Objeto "XMLHttpRequest" usado nas requisições.
   * @name _transport
   * @type XMLHttpRequest
   */
  var _transport = null;

  /** 
   * Função de callback usada para carregar JSON remotamente.
   * @name _callback
   * @type Function
   */
  var _callback = null;

  /** 
   * Estados da requisição.
   * @name _states
   * @type String[]
   */
  var _states = ['Uninitialized','Loading','Loaded','Interactive','Complete'];

  /**#@+ @function */

  /**
   * Envia a próxima requisição da fila.
   */  
  var _sendRequest = function() {
    var t = _transport, r = _requests.shift(), data;

    // Abre o objeto XMLHttpRequest
    t.open(r.type, r.url, r.async);

    //setRequestHeaders();

    // Registra o objeto para que o servidor saiba que é uma requisição AJAX
    t.setRequestHeader('X-Requested-With', 'XMLHttpRequest');

    // Caso tenha sido informado algum dado
    if (data = jProton.serialize(r.data))
      t.setRequestHeader('Content-Type', r.contentType);

    /** @ignore */
    // Tratamento de evento de mudança de estado
    t.onreadystatechange = function(){ 
        _onStateChange(r); 
      }; 

    // Envia a requisição
    t.send(data);
  };

  /**
   * Função de tratamento da mudança de estado da requisição ajax.
   */     
  var _onStateChange = function(options) {
    var fn, o = options, t = _transport;
    var state = _getState(t); 

    if (fn = o['on' + state]) fn(_getResponse(o), o);

    if (state == 'Complete') {
      var success = t.status == 200, response = _getResponse(o);

      if (fn = o['onUpdate'])
        fn(response, o);

      if (fn = o['on' + (success ? 'Success' : 'Failure')])
        fn(response, o);

      t.onreadystatechange = jProton.emptyFn;

      if (_requests.length > 0) 
        setTimeout(_sendRequest, 10);
    }
  };

  /**
   * Retorna a resposta de acordo com o tipo de dado requisitado.
   */  
  var _getResponse = function(options) {
    var t = _transport, type = options.dataType;

    if      (t.status != 200) return t.statusText
    else if (type == 'text')  return t.responseText
    else if (type == 'html')  return t.responseText
    else if (type == 'xml')   return t.responseXML
    else if (type == 'json')  return eval('(' + t.responseText + ')');
  };

  /**
   * Retorna o atual estado da requisição ajax.
   */     
  var _getState = function() {
    return _states[_transport.readyState];
  };

  /**#@-*/
  /**#@+ @extends ajax */

  return /** @lends jProton.ajax */ {
  
    '@extension': {'ajax': '0.3.0a'},
  
    /**
     * Inicializa o módulo ajax, criando o objeto "XMLHttpRequest".
     * @ignore
     */
    '@init': function() {
      _transport = null;
  
      try {
        _transport = new XMLHttpRequest();
      } catch (e) {
        try {
          _transport = new ActiveXObject("Msxml2.XMLHTTP");
        } catch (e) {
          try {
            _transport = new ActiveXObject("Microsoft.XMLHTTP");
          } catch (e) { }
        }
      }
    },
    
    /**
     * Realiza uma requisição ajax.
     * 
     * @param {Object}   options               Opções da requisição.  
     * @param {String}   options.url           URL a ser requisitada.
     * @param {String}   options.type          Tipo de requisição ("get" ou "post"). O padrão é "get".
     * @param {Boolean}  options.async         Indica se a requisição é assíncrona. O padrão é "true".   
     * @param {String}   options.dataType      Dado requisitado ("text", "html", "xml" ou "json"). O padrão é "text".
     * @param {String}   options.contentType   ContentType a ser usado. O padrão é "application/x-www-form-urlencoded".  
     * @param {Function} options.onLoading     Função a ser executada antes da requisição ser enviada.
     * @param {Function} options.onLoaded      Função a ser executada logo que a requisição for enviada.
     * @param {Function} options.onInteractive Função a ser executada durante o recebimento da requisição.
     * @param {Function} options.onComplete    Função a ser executada ao completar a requisição.
     * @param {Function} options.onUpdate      Função a ser executada após completar a requisição.
     * @param {Function} options.onSuccess     Função a ser executada ao completar a requisição com sucesso.
     * @param {Function} options.onError       Função a ser executada ao completar a requisição com erro.
     */      
    request: function(options) {
      var o = options || {};
  
      // Configura as opções que não foram definidas para o seu valor padrão
      o.type = o.type && o.type.toLowerCase() || 'get';
      o.async = o.async || true;
      o.dataType = o.dataType || 'text'; 
      o.contentType = o.contentType || 'application/x-www-form-urlencoded';
  
      _requests.push(o);
  
      var s = _getState();
      if (s == 'Uninitialized' || s == 'Complete') 
        _sendRequest();
    }  
  }
}());
/**#@-*/


// Extensão ajax para jProton.Set. Requer extensão dom de jProton.Set.
set.extend({ 

  '@require': {'dom': '0.3'},
  '@extension': {'ajax': '0.3.0a'},

  /**#@+
   * @function
   * @memberOf jProton.Set
   * @extends ajax
   */

  /**
   * Realiza requisição ajax e carrega o conteúdo nos elementos selecionados.
   *    
   * @param {String}   url       URL a ser requisitada.
   * @param {Data}     data      Dados a serem enviados.
   * @param {Function} callback  Função de retorno.
   * @name load
   */
  load: function(url, data, callback) {
    var d = data || null, fn = callback || null;

    if ((typeof data == 'function') && (typeof callback == 'undefined')) {
      d = null;
      fn = data;
    }

    ajax.request({url: url, data: d, onSuccess: fn,
      elements: this, 
      onUpdate: function(r,o){
        $(o.elements).html(r);
      }
    });

    return this;
  }

  /**#@-*/
});


// Extensão ajax para jProton.
jProton.extend({

  '@extension': {'ajax': '0.2.0a'},

  /**#@+
   * @function
   * @memberOf jProton
   * @extends ajax
   */

  /**
   * Serializa.
   * 
   * @name serialize
   * @param {Object} data Desc
   */
  serialize: function(data) {
  	var r = [''], rl = 0;
  	if (data) {
  		if (typeof data == 'string')	r[rl++] = data
  			
  		else if (data.innerHTML && data.elements) {
  			for (var i=0,el,l=(el=data.elements).length; i < l; i++)
  				if (el[i].name) {
  				  r[rl++] = encodeURIComponent(el[i].name); 
            r[rl++] = '=';
            r[rl++] = encodeURIComponent(el[i].value);
            r[rl++] = '&';
          }
  					
  		} else 
  			for(param in data) {
				  r[rl++] = encodeURIComponent(param); 
          r[rl++] = '=';
          r[rl++] = encodeURIComponent(data[param]);
          r[rl++] = '&';
        }
  	}
  	return r.join('').replace(/&$/, "");
  },

  /**
   * Realiza uma requisição ajax do tipo "get", retornando texto.
   *    
   * @name get
   * @param {String}   url       URL a ser requisitada.
   * @param {Data}     data      Dados a serem enviados.
   * @param {Function} callback  Função de retorno.
   */
  get: function(url, data, callback) {
    var d = data || null, fn = callback || null;

    if ((typeof data == 'function') && (typeof callback == 'undefined')) {
      d = null;
      fn = data;
    }

    ajax.request({url: url, data: d, onSuccess: fn});
  },

  /**
   * Realiza uma requisição ajax do tipo "get", retornando JSON.
   *
   * @name getJSON
   * @param {String}   url       URL a ser requisitada.
   * @param {Data}     data      Dados a serem enviados.
   * @param {Function} callback  Função de retorno.
   */
  getJSON: function(url, data, callback) {
    var d = data || null, fn = callback || null;

    if ((typeof data == 'function') && (typeof callback == 'undefined')) {
      d = null;
      fn = data;
    }

    ajax.request({url: url, data: d, dataType: 'json', onSuccess: fn});
  },

  /**
   * Realiza uma requisição ajax do tipo "get", retornando XML.
   *
   * @name getXML
   * @param {String}   url       URL a ser requisitada.
   * @param {Data}     data      Dados a serem enviados.
   * @param {Function} callback  Função de retorno.
   */
  getXML: function(url, data, callback) {
    var d = data || null, fn = callback || null;

    if ((typeof data == 'function') && (typeof callback == 'undefined')) {
      d = null;
      fn = data;
    }

    ajax.request({url: url, data: d, dataType: 'xml', onSuccess: fn});
  },

  /**
   * Realiza uma requisição ajax do tipo "post".
   *
   * @name post
   * @param {String}   url       URL a ser requisitada.
   * @param {Data}     data      Dados a serem enviados.
   * @param {Function} callback  Função de retorno.
   */
  post: function(url, data, callback) {
    var d = data || null, fn = callback || null;

    if ((typeof data == 'function') && (typeof callback == 'undefined')) {
      d = null;
      fn = data;
    }

    ajax.request({url: url, type: 'post', data: d, onSuccess: fn});
  },

  /**
   * Carrega um script dinamicamente.
   *    
   * @name loadScript
   * @param {String}   url       URL do script.
   */
  loadScript: function(url) {
    var script = document.getElementById('dynamicScript');

    if (script)
      script.parentNode.removeChild(script);

    script = document.createElement('script');
    script.setAttribute('src', url);
    script.setAttribute('id', 'dynamicScript');
    script.setAttribute('type', 'text/javascript');
    document.documentElement.firstChild.appendChild(script);
  },

  /**
   * Carrega JSON remotamente (JSONP).
   *    
   * @name loadJSONP
   * @param {String}   url       URL dos dados.
   * @param {String}   jsonp     Nome da função de retorno do servidor. O 
   *                             padrão é "callback".
   * @param {Function} callback  Função de retorno.
   */
  loadJSONP: function(url, jsonp, callback) {
    var j = jsonp || 'callback', fn = callback || jProton.emptyFn,
        script = document.getElementById('dynamicScript');

    if ((typeof jsonp == 'function') && (typeof callback == 'undefined')) {
      j = 'callback';
      fn = jsonp;
    }

    if (script)
      script.parentNode.removeChild(script);

    ajax._callback = fn;
    script = document.createElement('script');
    script.setAttribute('src', url + '&'+ j + '=jProton.ajax._callback');
    script.setAttribute('id', 'dynamicScript');
    script.setAttribute('type', 'text/javascript');
    document.documentElement.firstChild.appendChild(script);
  }

  /**#@-*/
});


/**
 * Namespace responsável pela seleção de elementos através de seletores CSS. 
 * Baseado em 
 * [[http://extjs.com/deploy/ext/docs/output/Ext.DomQuery.html|DomQuery]].
 * 
 * @name jProton.query
 * @namespace
 */
/** @ignore */
jProton.query = function(){
  /**#@+ 
   * @private 
   * @function
   * @memberOf jProton.query
   * @extends core
   */

  var cache = {}, 
    simpleCache = {}, 
    reTrim = /^\s+|\s+$/g,
    reTemplate = /\{(\d+)\}/g,
    reMode = /^(\s?[\/>+~]\s?|\s|$)/,
    reTag = /^(#)?([\w-\*]+)/,
    reNth = /(\d*)n\+?(\d*)/, 
    reNth2 = /\D/,

    // This is for IE MSXML which does not support expandos.
    // IE runs the same speed using setAttribute, however FF slows way down
    // and Safari completely fails so they need to continue to use expandos.
    isIE = !!window.ActiveXObject,
    key = 30803;

  // this eval is stop the compressor from
  // renaming the variable to something shorter
  eval("var batch = 30803;");

  function _quickId(e, mode, context, val){
    if(e == context){
      var d = context.ownerDocument || context;
      return d.getElementById(val);
    }
    e = _getNodes(e, mode, "*");
    return _byId(e, val);
  };

  var _byId = function(e, val){
    if(e.tagName || e == document)
      e = [e];

    var r = new Array(), rl = -1;
    for(var i = 0, ei; ei = e[i]; i++)
      if(ei && ei.id == val){
        r[++rl] = ei;
        return r;
      }
    return r;
  };

  var _byTag = function(e, val){
    if(e.tagName || e == document)
      e = [e];

    var r = new Array(), rl = -1;
    val = val.toLowerCase();
    for(var i = 0, ei; ei = e[i]; i++)
      if(ei.nodeType == 1 && ei.tagName.toLowerCase()==val)
        r[++rl] = ei;

    return r;
  };

  var _byClass = function(e, val){
    if(!val)
      return e;

    var r = new Array(), rl = -1;
    for(var i = 0, ei; ei = e[i]; i++)
      if((' '+ei.className+' ').indexOf(val) != -1)
        r[++rl] = ei;

    return r;
  };

  var _byAttr = function(e, attr, op, val){
    var r = new Array(), rl = -1, f = query.operators[op];
    for(var i = 0, ei, a; ei = e[i]; i++){
      if(attr == "class" || attr == "className")
        a = ei.className;
      else if(attr == "for")
        a = ei.htmlFor;
      else if(attr == "href")
        a = ei.getAttribute("href", 2)
      else
        a = ei.getAttribute(attr);

      if((f && f(a, val)) || (!f && a))
        r[++rl] = ei;
    }
    return r;
  };

  var _byPseudo = function(e, name, val){
    return query.pseudos[name](e, val);
  };

  var _getNodes = function(e, mode, name){
    var r = new Array(), rl = -1;
    if(!e)
      return r;

    name = name || "*";
    if(typeof e.getElementsByTagName != "undefined")
      e = [e];

    if(!mode){
      for(var i = 0, ei, n; ei = e[i]; i++){
        n = ei.getElementsByTagName(name);
        for(var j = 0, nj; nj = n[j]; j++)
          r[++rl] = nj;
      }
    }else if(mode == "/" || mode == ">"){
      var utag = name.toUpperCase();
      for(var i = 0, ei, n; ei = e[i]; i++){
        n = ei.children || ei.childNodes;
        for(var j = 0, nj; nj = n[j]; j++)
          if(nj.nodeName == utag || nj.nodeName == name  || name == '*')
            r[++rl] = nj;

      }
    }else if(mode == "+"){
      var utag = name.toUpperCase();
      for(var i = 0, ei; ei = e[i]; i++){
        while((ei = ei.nextSibling) && ei.nodeType != 1);
        if(ei && (ei.nodeName == utag || ei.nodeName == name || name == '*'))
          r[++rl] = ei;
      }
    }else if(mode == "~"){
      for(var i = 0, ei; ei = e[i]; i++){
        while((ei = ei.nextSibling) && (ei.nodeType != 1 || 
              (name == '*' || ei.tagName.toLowerCase()!=name)));
        if(ei)
          r[++rl] = ei;
      }
    }
    return r;
  };

  var _unique = function(e){
    if(!e)
      return new Array();

    var len = e.length, i, r = e, ei;
    if(!len || typeof e.nodeType != "undefined" || len == 1)
      return e;

    if(isIE && typeof e[0].selectSingleNode != "undefined") {
      var d = ++key;
      e[0].setAttribute("__unique", d);
      var r = [e[0]], rl = 0;
      for(var i = 1, ei; ei = e[i]; i++)
        if(!ei.getAttribute("__unique") != d){
          ei.setAttribute("__unique", d);
          r[++rl] = ei;
        }
  
      for(var i = 0; ei = e[i]; i++)
        ei.removeAttribute("__unique");
  
      return r;
    }

    var d = ++key;
    e[0].__unique = d;
    for(i = 1, ei; ei = e[i]; i++){
      if(ei.__unique != d)
        ei.__unique = d;
      else{
        r = new Array(), rl = -1;
        for(var j = 0; j < i; j++)
          r[++rl] = e[j];

        for(j = i+1; ej = e[j]; j++)
          if(ej.__unique != d){
            ej.__unique = d;
            r[++rl] = ej;
          }
        return r;
      }
    }
    return r;
  };

  var _diff = function(e, e2){
    if(!e.length)
      return e2;

    if(isIE && e[0].selectSingleNode) {
      var d = ++key, ei;
      for(var i = 0; ei = e[i]; i++)
        ei.setAttribute("__diff", d);
  
      var r = new Array(), rl = -1;
      for(var i = 0; ei = e2[i]; i++)
        if(ei.getAttribute("__diff") != d)
          r[++rl] = ei;
  
      for(var i = 0; ei = e[i]; i++)
        ei.removeAttribute("__diff");
  
      return r;
    }

    var d = ++key;
    for(var i = 0; ei = e[i]; i++)
      ei.__diff = d;

    var r = new Array(), rl = -1;
    for(var i = 0; ei = e2[i]; i++)
      if(ei.__diff != d)
        r[++rl] = ei;

    return r;
  };

  var _next = function(e){
    while((e = e.nextSibling) && e.nodeType != 1);
    return e;
  };

  var _prev = function(e){
    while((e = e.previousSibling) && e.nodeType != 1);
    return e;
  };

  /**#@-*/
  /**#@+ @extends core */
  return /** @lends jProton.query */ {
    /**
     * Compiles a selector/xpath query into a reusable function. The returned function
     * takes one parameter "context" (optional), which is the context node from where the query should start.
     * @param {String} selector The selector/xpath query
     * @param {String} type (optional) Either "select" (the default) or "simple" for a simple selector match
     * @return {Function}
     */
    compile: function(selector, type){
      type = type || "select";

      var s = selector, ls,
        rules = query.rules,
        mm, tm, rm, matched, fl = 0, 
        f = ["var fn=function(c){\nvar mode; var e=c||document; ++batch;\n"];

      // accept leading mode switch
      mm = s.match(reMode);
      if(mm && mm[1]){
        f[++fl] = 'mode="'+mm[1].replace(reTrim, "")+'";';
        s = s.replace(mm[1], "");
      }

      // strip leading slashes
      while(s.substr(0, 1)=="/")
        s = s.substr(1);

      while(s && ls != s){
        ls = s;
        tm = s.match(reTag);
        if(type == "select"){
          if(tm){
            if(tm[1] == "#")
              f[++fl] = 'e = _quickId(e, mode, c, "'+tm[2]+'");';
            else
              f[++fl] = 'e = _getNodes(e, mode, "'+tm[2]+'");';

            s = s.replace(tm[0], "");
          }else if(s.substr(0, 1) != '@')
            f[++fl] = 'e = _getNodes(e, mode, "*");';

        }else
          if(tm){
            if(tm[1] == "#")
              f[++fl] = 'e = _byId(e, "'+tm[2]+'");';
            else
              f[++fl] = 'e = _byTag(e, "'+tm[2]+'");'

            s = s.replace(tm[0], "");
          }

        while(!(mm = s.match(reMode))){
          matched = false;
          for(var j = 0; rm = rules[j]; j++){
            var m = s.match(rm.re);
            if(m){
              f[++fl]=rm.select.replace(reTemplate, function(e,i){return m[i];});
              s = s.replace(m[0], "");
              matched = true;
              break;
            }
          }
          // prevent infinite loop on bad selector
          if(!matched)
            throw 'Error parsing selector, parsing failed at "' + s + '"';
        }
        if(mm[1]){
          f[++fl] = 'mode="'+mm[1].replace(reTrim, "")+'";';
          s = s.replace(mm[1], "");
        }
      }
      f[++fl] = "return _unique(e);\n}";
      eval(f.join(""));
      return fn;
    },

    /**
     * Selects a group of elements.
     * @param {String} selector The selector/xpath query (can be a comma separated list of selectors)
     * @param {Node} context (optional) The start of the query (defaults to document).
     * @return {Array}
     */
    select: function(selector, context){
      context = context || document;

      if(typeof context == "string")
        context = document.getElementById(context);

      var selectors = selector.split(","), len = selectors.length, results = new Array();
      for(var i = 0; i < len; i++){
        var s = selectors[i].replace(reTrim, "");
        if(!cache[s])
          if(!(cache[s] = query.compile(s)))
            throw s + " is not a valid selector";

        var result = cache[s](context);
        if(result && result != document)
          results = results.concat(result);
      }
      if(len > 1)
        return _unique(results);

      return results;
    },

    /**
     * Returns true if the passed element(s) match the passed simple selector (e.g. div.some-class or span:first-child)
     * @param {String/HTMLElement/Array} e An element id, elvalent or array of elements
     * @param {String} selector The simple selector to test
     * @return {Boolean}
     */
    is: function(e, ss){
      if(typeof e == "string")
        e = document.getElementById(e);

      var isArray = !!e.splice,
          result = query.filter(isArray ? e : [e], ss);
      return isArray ? (result.length == e.length) : (result.length > 0);
    },

    /**
     * Filters an array of elements to only include matches of a simple selector (e.g. div.some-class or span:first-child)
     * @param {Array} e An array of elements to filter
     * @param {String} selector The simple selector to test
     * @param {Boolean} nonMatches If true, it returns the elements that DON'T match
     * the selector instead of the ones that match
     * @return {Array}
     */
    filter: function(e, ss, nonMatches){
      ss = ss.replace(reTrim, "");
      if(!simpleCache[ss])
        simpleCache[ss] = query.compile(ss, "simple");

      var result = simpleCache[ss](e);
      return nonMatches ? _diff(result, e) : result;
    },

    /**
     * Collection of matching regular expressions and code snippets.
     */
    rules: [{
        re: /^\.([\w-]+)/,
        select: 'e = _byClass(e, " {1} ");'
      }, {
        re: /^\:([\w-]+)(?:\(((?:[^\s>\/]*|.*?))\))?/,
        select: 'e = _byPseudo(e, "{1}", "{2}");'
      },{
        re: /^\[([\w-]+)(?:(.?=)["']?(.+?)["']?)?\]/,
        select: 'e = _byAttr(e, "{1}", "{2}", "{3}");'
      }, {
        re: /^#([\w-]+)/,
        select: 'e = _byId(e, "{1}");'
      }
    ],

    /**
     * Collection of operator comparison functions. The default operators are =, !=, ^=, $=, *=, %=, |= and ~=.
     * New operators can be added as long as the match the format <i>c</i>= where <i>c</i> is any character other than space, &gt; &lt;.
     */
    operators: {
      "=": function(attr, val){
        return attr == val;
      },
      "!=": function(attr, val){
        return attr != val;
      },
      "^=": function(attr, val){
        return attr && attr.substr(0, val.length) == val;
      },
      "$=": function(attr, val){
        return attr && attr.substr(attr.length-val.length) == val;
      },
      "*=": function(attr, val){
        return attr && attr.indexOf(val) !== -1;
      },
      "%=": function(attr, val){
        return (attr % val) == 0;
      },
      "|=": function(attr, val){
        return attr && (attr == val || attr.substr(0, val.length+1) == val+'-');
      },
      "~=": function(attr, val){
        return attr && (' '+attr+' ').indexOf(' '+val+' ') != -1;
      }
    },

    /**
     * Collection of "pseudo class" processors. Each processor is passed the current nodeset (array)
     * and the argument (if any) supplied in the selector.
     */
    pseudos: {
      "first-child": function(e){
        var r = new Array(), rl = -1, n;
        for(var i = 0, ei; ei = n = e[i]; i++){
          while((n = n.previousSibling) && n.nodeType != 1);
          if(!n)
            r[++rl] = ei;
        }
        return r;
      },

      "last-child": function(e){
        var r = new Array(), rl = -1, n;
        for(var i = 0, ei; ei = n = e[i]; i++){
          while((n = n.nextSibling) && n.nodeType != 1);
          if(!n)
            r[++rl] = ei;
        }
        return r;
      },

      "nth-child": function(e, val) {
        var r = new Array(), rl = -1, m = reNth.exec(val == "even" && "2n" || 
          val == "odd" && "2n+1" || !reNth2.test(val) && "n+" + val || val),
          f = (m[1] || 1) - 0, l = m[2] - 0;
        for(var i = 0, ei; ei = e[i]; i++){
          var p = ei.parentNode;
          if (batch != p._batch) {
            var j = 0;
            for(var n = p.firstChild; n; n = n.nextSibling)
              if(n.nodeType == 1)
                 n.nodeIndex = ++j;

            p._batch = batch;
          }
          if (f == 1) {
            if (l == 0 || ei.nodeIndex == l)
              r[++rl] = ei;

          } else if ((ei.nodeIndex + l) % f == 0)
            r[++rl] = ei;
        }

        return r;
      },

      "only-child": function(e){
        var r = new Array(), rl = -1;;
        for(var i = 0, ei; ei = e[i]; i++)
          if(!_prev(ei) && !_next(ei))
            r[++rl] = ei;

        return r;
      },

      "empty": function(e){
        var r = new Array(), rl = -1;
        for(var i = 0, ei; ei = e[i]; i++){
          var n = ei.childNodes, j = -1, nj, empty = true;
          while(nj = n[++j])
            if(nj.nodeType == 1 || nj.nodeType == 3){
              empty = false;
              break;
            }

          if(empty)
            r[++rl] = ei;
        }
        return r;
      },

      "contains": function(e, val){
        var r = new Array(), rl = -1;
        for(var i = 0, ei; ei = e[i]; i++)
          if((ei.textContent||ei.innerText||'').indexOf(val) != -1)
            r[++rl] = ei;

        return r;
      },

      "nodeValue": function(e, val){
        var r = new Array(), rl = -1;
        for(var i = 0, ei; ei = e[i]; i++)
          if(ei.firstChild && ei.firstChild.nodeValue == val)
            r[++rl] = ei;

        return r;
      },

      "checked": function(e){
        var r = new Array(), rl = -1;
        for(var i = 0, ei; ei = e[i]; i++)
          if(ei.checked == true)
            r[++rl] = ei;

        return r;
      },

      "not": function(e, val){
        return query.filter(e, val, true);
      },

      "any": function(e, val){
        var r = new Array(), rl = -1, s, ss = val.split('|');
        for(var i = 0, ei; ei = e[i]; i++)
          for(var j = 0; s = ss[j]; j++)
            if(query.is(ei, s)){
              r[++rl] = ei;
              break;
            }

        return r;
      },

      "odd": function(e){
        return this["nth-child"](e, "odd");
      },

      "even": function(e){
        return this["nth-child"](e, "even");
      },

      "nth": function(e, val){
        return e[val-1] || new Array();
      },

      "first": function(e){
        return e[0] || new Array();
      },

      "last": function(e){
        return e[e.length-1] || new Array();
      },

      "has": function(e, val){
        var r = new Array(), rl = -1, s = query.select;
        for(var i = 0, ei; ei = e[i]; i++)
          if(s(val, ei).length > 0)
            r[++rl] = ei;

        return r;
      },

      "next": function(e, val){
        var r = new Array(), rl = -1, is = query.is;
        for(var i = 0, ei; ei = e[i]; i++){
          var n = _next(ei);
          if(n && is(n, val))
            r[++rl] = ei;
        }
        return r;
      },

      "prev": function(e, val){
        var r = new Array(), rl = -1, is = query.is;
        for(var i = 0, ei; ei = e[i]; i++){
          var n = _prev(ei);
          if(n && is(n, val))
            r[++rl] = ei;
        }
        return r;
      }
    }
  }
}();

// Atalho para jProton.query
var query = jProton.query;

})();